#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <Windows.h>
#include <io.h>
#include "MarkupSTL.h"
#include "Neo4j_Node.h"

using namespace std;

class Graph {
public:
	string gid;
	boolean isDirected;
	map<string, Neo4j_Node*> nodes;
	map<string, Neo4j_Edge*> edges;

	Graph(string _gid, boolean _isDirected) {
		gid = _gid;
		isDirected = _isDirected;
	}
};

class Parser4GraphML {
public:
	Graph* generate_graph(const char *grahpml_file_path) {
		if (grahpml_file_path == nullptr || !is_file_exist(grahpml_file_path)) {
			return nullptr;
		}
		markup_ptr.Load(grahpml_file_path);
		markup_ptr.FindElem("graphml");
		markup_ptr.IntoElem();
		if (markup_ptr.FindElem("graph")) {
			Graph* graph = new Graph(markup_ptr.GetAttrib("id"), markup_ptr.GetAttrib("edgedefault") == "directed");
			markup_ptr.IntoElem();
			while (markup_ptr.FindElem()) {
				if (markup_ptr.GetTagName() == "node") {
					string nid = markup_ptr.GetAttrib("id");
					string label = markup_ptr.GetAttrib("labels");
					Neo4j_Node *newNode = new Neo4j_Node(nid, label.substr(1, label.length() - 1));
					markup_ptr.IntoElem();
					while (markup_ptr.FindElem("data")) {
						if (markup_ptr.GetAttrib("key") == "labels") {
							continue;
						}
						newNode->node_attritubes.insert(pair <string, string>(markup_ptr.GetAttrib("key"), markup_ptr.GetData()));
					}
					graph->nodes.insert(pair <string, Neo4j_Node*>(nid, newNode));
					markup_ptr.OutOfElem();
				}
				else if (markup_ptr.GetTagName() == "edge") {
					string eid = markup_ptr.GetAttrib("id");
					string label = markup_ptr.GetAttrib("label");
					Neo4j_Edge *newEdge = new Neo4j_Edge(eid, label);
					Neo4j_Node *source_node = graph->nodes.find(markup_ptr.GetAttrib("source"))->second;
					Neo4j_Node *target_node = graph->nodes.find(markup_ptr.GetAttrib("target"))->second;
					source_node->out++;
					source_node->nexts.push_back(target_node);
					source_node->next_edges.push_back(newEdge);
					target_node->in++;
					markup_ptr.IntoElem();
					while (markup_ptr.FindElem("data")) {
						if (markup_ptr.GetAttrib("key") == "labels") {
							continue;
						}
						newEdge->edge_attritutes.insert(pair <string, string>(markup_ptr.GetAttrib("key"), markup_ptr.GetData()));
					}
					graph->edges.insert(pair <string, Neo4j_Edge*>(eid, newEdge));
					markup_ptr.OutOfElem();
				}
			}
			markup_ptr.OutOfElem();
			return graph;
		}
		else {
			return nullptr;
		}
	}

private:
	CMarkupSTL markup_ptr;

	bool is_file_exist(const char* path) {
#ifdef _WIN32
		return _access(path, 0) == 0;
#else
		return access(path, R_OK | W_OK) == 0;
#endif
	}
};

